Pythonでサンプルデータを生成してOpenSearchに取り込んでみよう!

OpenSearchを触り始めるときや、新しい機能の検証をしたいとき、「手頃なサンプルデータがないかな?」って思うこと、ありますよね。

というわけで今回は、サンプルデータをPythonでサクッと生成して、Dockerで動いているOpenSearchに投入するまでの手順を紹介していきたいと思います! この記事では、データ生成に用いたPythonスクリプトの要点も解説します。

前回、作成した環境をベースにしていますので、こちらも一緒にご覧くだい。

どんなデータを作るの?

まずは、どんなデータを作るか見ていきましょう。今回は、ECサイトで扱われるような商品データを想定しています。カテゴリ、ブランド、商品名、価格といった基本的な情報に加えて、評価やタグ、簡単なスペック情報も持たせて、検索の検証に使いやすいデータを目指します。

データ生成にはPythonスクリプトを使用しました。全文を載せると長くなってしまうので、ここではスクリプトのポイントを解説しますね。

データ生成のスクリプトはこちらで確認できます。

データ生成スクリプトのポイント

このスクリプトは、大きく分けて「①データの定義部分」と「②データを生成するロジック部分」の2つで構成されています。

① データの定義:リアルさを出すための土台

スクリプトの冒頭では、生成するデータの元となる情報をPythonの辞書型で定義しています。

  • 階層的なカテゴリ定義 (categories): 「大カテゴリ > 中カテゴリ > 詳細カテゴリ」という階層構造を定義しています。これにより、ファセット検索(絞り込み検索)の検証に使いやすい、構造化されたデータが作れます。
categories = {
    '家電・デジタル': {
        'コンピューター': ['ノートパソコン', 'デスクトップPC', ...],
        'モバイル機器': ['スマートフォン', 'ガラケー', ...],
        ...
    },
    'ホーム・ガーデン': {
        ...
    }
}

カテゴリに応じたブランド (brands): 例えば「家電・デジタル」カテゴリには「ソニー」や「パナソニック」、「ファッション」カテゴリには「ナイキ」や「ユニクロ」といった、関連性の高いブランドが割り当てられるようになっています。これにより、カテゴリとブランドの紐付きがそれっぽくなります。

brands = {
    '家電・デジタル': ['アップル', 'サムスン', 'ソニー', ...],
    'ホーム・ガーデン': ['イケア', 'ハーマンミラー', ...],
    'ファッション': ['ナイキ', 'アディダス', 'ユニクロ', ...],
    ...
}

それっぽい商品名 (product_templates): 特定のカテゴリに対して、よくある商品名のテンプレートを用意しています。これにブランド名や接尾辞(プロ, プラスなど)を組み合わせることで、ランダムながらも「ありそう」な商品名を生成しています。

product_templates = {
    'ノートパソコン': ['MacBook Pro', 'ThinkPad', 'XPS', ...],
    'スマートフォン': ['iPhone', 'Galaxy', 'Pixel', ...],
    ...
}

② データ生成ロジック:定義を元にデータを組み立てる

定義したデータを元に、実際の製品データを一件ずつ組み立てていくのがロジック部分です。

  • 各種生成関数 (generate_...): 商品名、価格、説明文を生成するための専用関数を用意しています。例えば価格を生成する generate_price 関数では、カテゴリごとに価格帯の目安を設けることで、「スニーカーが200万円」のような、ありえないデータが作られるのを防いでいます。
def generate_price(main_cat, detail_cat):
    price_ranges = {
        'ノートパソコン': (50000, 500000),
        'スマートフォン': (30000, 200000),
        ...
    }
    # ... カテゴリに応じた価格を生成するロジック ...
    return random.randint(min_price, max_price)

メインの生成ループ: 定義したカテゴリを順番に処理していき、各カテゴリに一定数の商品データが割り振られるようにループ処理をしています。これにより、データが特定のカテゴリに偏るのを防ぎ、バランスの取れたデータセットを作成します。

products = []
product_id = 1

for main_cat, sub_cats in categories.items():
    for sub_cat, detail_cats in sub_cats.items():
        for detail_cat in detail_cats:
            # ... この中で各製品データを生成 ...

多様なデータ型: ループの中で、価格(数値)、在庫(ブール値)、作成日(日付)、評価(浮動小数点数)、タグ(配列)など、様々なデータ型をランダムに生成して盛り込んでいます。これにより、OpenSearchの多様なクエリや集計機能のテストが可能になります。

product = {
    'name': name,
    'description': description,
    'price': price,
    'main_category': main_cat,
    'sub_category': sub_cat,
    'detail_category': detail_cat,
    'brand': brand,
    'in_stock': in_stock, # bool
    'created_at': created_date.strftime('%Y-%m-%d'), # date
    'rating': rating, # float
    'tags': tags # array
}

Bulk API形式での出力: 最後に、生成した全データをOpenSearchのBulk APIが要求するJSON形式(一行目にメタデータ、二行目にデータ本体)でファイルに出力します。

with open('bulk_products_1000_japanese.json', 'w', encoding='utf-8') as f:
    for i, product in enumerate(products, 1):
        index_line = json.dumps({'index': {'_index': 'products_jp', '_id': str(i)}})
        product_line = json.dumps(product, ensure_ascii=False)
        f.write(index_line + '\n')
        f.write(product_line + '\n')

このように、いくつかのルールやテンプレートを組み合わせることで、手軽にリアルなテストデータを作成することができます。

出来上がったデータはこんな感じ

スクリプトを実行すると、bulk_products_1000_japanese.json というファイルが出力されます。中身はこんな感じです。

{"index": {"_index": "products_jp", "_id": "1"}}
{"name": "ニコン ZenBook ウルトラ", "description": "高性能で信頼性の高い、先進的な機能を搭載したノートパソコン", "price": 191716, "main_category": "家電・デジタル", "sub_category": "コンピューター", "detail_category": "ノートパソコン", "brand": "ニコン", "in_stock": false, "created_at": "2025-05-29", "rating": 4.8, "tags": ["ノートパソコン", "ニコン", "高級品", "ベストセラー"], "specifications": {"プロセッサー": "AMD Ryzen 7", "メモリ": "8GB", "ストレージ": "1TB"}}
{"index": {"_index": "products_jp", "_id": "2"}}
{"name": "LG Surface Laptop プロ", "description": "高性能で信頼性の高い、先進的な機能を搭載したノートパソコン", "price": 330582, "main_category": "家電・デジタル", "sub_category": "コンピューター", "detail_category": "ノートパソコン", "brand": "LG", "in_stock": false, "created_at": "2025-05-05", "rating": 3.9, "tags": ["ノートパソコン", "LG", "高級品", "ベストセラー"], "specifications": {"プロセッサー": "M3", "メモリ": "8GB", "ストレージ": "512GB"}}
...

一行目に index 情報、二行目にドキュメント本体、という形式が繰り返されていますね。これはOpenSearchのBulk APIで一括登録するための専用フォーマットなんです。この形式のおかげで、大量のデータも効率的に投入できます。

Docker上のOpenSearchにデータを取り込む

データが準備できたら、いよいよOpenSearchに取り込んでいきましょう。ここでは、DockerでOpenSearch環境がすでに起動している前提で進めます。

ステップ1: JSONファイルをコンテナにコピー

まずは、ローカルに生成したJSONファイルを、動いているOpenSearchのコンテナにコピーします。使うのは docker cp コマンドです。

docker cp "[local path]\bulk_products_1000_japanese.json" opensearch-node1:/usr/share/opensearch/

[local path] の部分には、bulk_products_1000_japanese.json があるローカルのパスを指定してください。`opensearch-node1` はお使いのコンテナ名に合わせて変更してくださいね。

ステップ2: Bulk APIでデータ投入

ファイルがコンテナ内に配置できたら、あとは `curl` コマンドでBulk APIを叩くだけです。コンテナ内でコマンドを実行するために `docker exec` を使います。

docker exec opensearch-node1 curl -XPOST "https://localhost:9200/_bulk" -H "Content-Type: application/json" -u "admin:MyStrongPassword123!" -k --data-binary "@/usr/share/opensearch/bulk_products_1000_japanese.json"

このコマンド、ちょっと長いですが、やっていることはシンプルです。 opensearch-node1 コンテナの中で curl コマンドを実行し、_bulk エンドポイントに対して、先ほどコピーしたJSONファイル (--data-binary) を送信しています。 -u で認証情報を、-k で証明書の検証をスキップするお決まりのオプションもつけています。

コマンドが成功すれば、JSON形式のレスポンスが返ってきて、データ投入は完了です!

おわりに

これで、OpenSearchに1000件の商品サンプルデータが入りました。OpenSearch Dashboardsなどで実際にデータを確認してみてください。 今回紹介したPythonスクリプトのカテゴリやブランドの定義を変えれば、自分だけのカスタムデータも簡単に作れます。色々と試してみてください。